package ch.ethz.fcl.metrobuzz.data.singapore;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.data.transit.Stop;

public class SGMRTStations {
	private Singapore sing = null;
	// EW24/NS1 is one stop, while two nodes
	private Map<String, String> nodeIdToName;
	private Map<String, String> stopIdToName;
	private List<StationDistance> stationDistList;
	private Map<String, Float> stationDistMap;

	public SGMRTStations(Singapore sing) {
		this.sing = sing;
		nodeIdToName = new HashMap<String, String>();
	}

	public void putName(String id, String name) {
		if (!nodeIdToName.containsKey(id)) {
			nodeIdToName.put(id, name);
		}
	}

	public float getDistance(String start, String end) {
		return stationDistMap.get(start + end);
	}

	// construct the distance between stations
	public void init() {
		MBData data = sing.getData();

		stopIdToName = new HashMap<String, String>();
		for (String id : nodeIdToName.keySet()) {
			Stop stop = data.getPublicTransit().getStopByStopId(id);
			if (!stopIdToName.containsKey(stop.getId()))
				stopIdToName.put(stop.getId(), nodeIdToName.get(id));
		}

		// System.out.println("There are " + stopIdToName.size() + " stations");

		stationDistList = new ArrayList<StationDistance>();

		for (String id : stopIdToName.keySet()) {
			Node node = data.getScene().getNode(id);
			StationDistance sd = new StationDistance(node.getId(), node.getId());
			sd.dist = 0;
			if (!stationDistList.contains(sd)) {
				stationDistList.add(sd);
				expandNode(node, node, 0);
			}
		}

		stationDistMap = new HashMap<String, Float>();
		for (StationDistance sd : stationDistList) {
			float realDist = data.getScene().getBound().getWorldDist(sd.dist);
			stationDistMap.put(sd.startID + sd.endID, realDist);
		}
	}

	private void expandNode(Node startNode, Node currentNode, float dist) {
		List<Edge> incomingEdges = currentNode.getInEdges();
		for (Edge e : incomingEdges) {
			float newDist = e.length() + dist;
			StationDistance sd = new StationDistance(e.getStartNode().getId(),
					startNode.getId());
			sd.dist = newDist;
			int index = contains(stationDistList, sd);

			if (index == -1) {
				stationDistList.add(sd);
				expandNode(startNode, e.getStartNode(), newDist);
			} else if (newDist < stationDistList.get(index).dist) {
				stationDistList.get(index).dist = newDist;
				expandNode(startNode, e.getStartNode(), newDist);
			}
		}
	}

	private int contains(List<StationDistance> stationDistList,
			StationDistance stationDist) {
		for (int i = 0; i < stationDistList.size(); i++)
			if (stationDistList.get(i).equals(stationDist))
				return i;
		return -1;
	}

	public int size() {
		return nodeIdToName.size();
	}

	public String getName(String id) {
		String[] stations = id.split("/");

		return nodeIdToName.get(stations[0]);
	}

	public static boolean isMRTStation(String id) {
		if (id.startsWith("EW") || id.startsWith("NS") || id.startsWith("CC")
				|| id.startsWith("NE") || id.startsWith("CG"))
			return true;
		else
			return false;
	}

	// check if the input nodes are on the same line
	public static boolean isSameLine(Node... nodes) {
		if (isSameLine("EW", nodes))
			return true;

		if (isSameLine("CG", nodes))
			return true;

		if (isSameLine("NS", nodes))
			return true;

		if (isSameLine("NE", nodes))
			return true;

		if (isSameLine("CC", nodes))
			return true;

		return false;
	}

	private static boolean isSameLine(String lineID, Node... nodes) {
		for (Node n : nodes)
			if (!n.getId().contains(lineID))
				return false;

		return true;
	}

	public static boolean isMRTLineStation(String id, String lineName) {
		if (id.toUpperCase().contains(lineName))
			return true;
		else
			return false;
	}

	class StationDistance {
		String startID, endID;
		float dist;

		StationDistance(String start, String end) {
			this.startID = start;
			this.endID = end;
		}

		@Override
		public boolean equals(Object o) {
			if (!(o instanceof StationDistance))
				return false;
			StationDistance sd = (StationDistance) o;
			if (startID.equals(sd.startID) && endID.equals(sd.endID))
				return true;
			return false;
		}
	}
}
